import os
import time
import datetime
import uuid
import signal
import subprocess
from . import constants
from rest_framework.generics import GenericAPIView
from django.http import JsonResponse, HttpResponse
from rest_framework.pagination import PageNumberPagination
from django.db.models import Q
from app_logging import LOGGER
from celery_app.task1 import regression_run_test, base_run_test
# from django.forms.models import model_to_dict
from django.shortcuts import render
from global_function import string_to_list
from .my_serializer import *
from .models import *
from .lib.redis_connection import fetch_redis
from rabbitmq.production_task import RabbitConnection, send_task

success_response = {'code': 200, 'status': 'success'}
fail_response = {'code': 200, 'status': 'fail'}


def login_auth(level=1):
    def wrapper(func):
        def inner(*args, **kwargs):
            try:
                token = args[0].request.META.get('HTTP_TOKEN')
            except Exception:
                token = ''
            if token:
                # 检查是否已登录
                redis = fetch_redis()
                if not redis.exists(token):
                    return JsonResponse({'code': 402, 'status': 'fail', 'message': '请先登录账号'})
                # 获取token 注册时间及level
                user_time = redis.hget(token, 'time')
                user_level = redis.hget(token, 'level')
                if time.time() - float(user_time) > 1800:
                    # 有效时间为30min
                    return JsonResponse({'code': 401, 'status': 'fail', 'message': '身份已失效！'})
                if int(user_level) < level:
                    return JsonResponse({'code': 403, 'status': 'fail', 'message': '没有权限访问'})
                # 通过认证, 继续执行
                redis.hset(token, 'time', time.time())
                return func(*args, **kwargs)
            return JsonResponse({'code': 402, 'status': 'fail', 'message': '请先登录账号'})

        return inner

    return wrapper


class TestConnect(GenericAPIView):
    def get(self, request):
        return render(request, 'test.html')


class LoginManage(GenericAPIView):
    """
    用户登录/注销
    """

    def post(self, request):
        """ 登录 """
        user_info = request.data.get('user_info')
        if not user_info:
            return
        user = UserInfo.objects.filter(username=user_info.get('username'), password=user_info.get('password')).first()
        if user:
            serializer = UserInfoSerializers(instance=user)
            _data = dict(serializer.data)
            # 生成token
            token = str(uuid.uuid4())
            save_redis = {
                'time': time.time(),
                'level': _data.get('level'),
            }
            redis = fetch_redis()
            # 写入内存
            redis.hmset(token, save_redis)
            data = {'status': 'success',
                    'code': 200,
                    'token': token,
                    'username': _data.get('username'),
                    'nickname': _data.get('nickname'),
                    'message': '登录成功'
                    }
            return JsonResponse(data)
        return JsonResponse({'status': 'fail', 'message': '用户名或密码错误!'})

    def delete(self, request):
        """ 注销 """
        token = request.META.get("HTTP_TOKEN")
        if not token:
            return JsonResponse({'status': 'fail', 'message': '您还未登录账户'})
        # 确认是否保持在登录状态中
        redis = fetch_redis()
        # 不管token 是否过期, 注销操作都返回成功
        if redis.exists(request.META.get('HTTP_TOKEN')):
            redis.delete(request.META.get('HTTP_TOKEN'))
        return JsonResponse({'status': 'success', 'message': '注销成功'})


class UserManage(GenericAPIView):
    """
    用户管理
    """

    @login_auth()
    def get(self, request):
        """
        获取所有用户信息
        :param request:
        :return:
        """
        _data = []
        params = request.query_params.dict()
        _resp = UserInfo.objects.all().order_by('-uid')
        # 分页处理
        total = len(_resp)
        page = PageNumberPagination()
        page.page_size = params.get('page_size', '20')
        page_list = page.paginate_queryset(_resp, request, view=self)
        serializers = UserInfoSerializers(instance=page_list, many=True)
        for m in serializers.data:
            _data.append(dict(m))
        resp = JsonResponse({'code': 200, 'total': total, 'data': _data})
        return resp

    @login_auth(4)
    def post(self, request):
        """
        新增用户
        :param request:
        :return:
        """
        data = request.data
        if not data.get('level'):
            return JsonResponse({'status': 'fail', 'message': '必须添加level级别'})
        user = UserInfo.objects.filter(username=data.get('username')).first()
        if user:
            return JsonResponse({'status': 'fail', 'message': '用户名已存在'})
        serializer = UserInfoSerializers(data=data)
        if serializer.is_valid():
            serializer.save()
        else:
            return JsonResponse({'status': 'fail', 'message': serializer.errors})
        return JsonResponse({'status': 'success', 'message': 'create success'})

    @login_auth(4)
    def put(self, request):
        data = request.data
        uid = data.get('uid')
        if not uid:
            return JsonResponse({'status': 'fail', 'msg': '请指定的用户名不存在'})
        info = UserInfo.objects.filter(uid=uid).first()
        if not info:
            fail_response['error'] = f'UID[{uid}]不存在!'
            return JsonResponse(fail_response)
        serializer = UserInfoSerializers(instance=info, data=data)
        if serializer.is_valid():
            serializer.save()
            _data = serializer.data
            # 更新token
            token = request.META.get('HTTP_TOKEN')
            redis = fetch_redis()
            save_redis = {
                'time': time.time(),
                'level': _data.get('level'),
            }
            redis.hmset(token, save_redis)
        else:
            return JsonResponse({'status': 'fail', 'error': serializer.errors})
        return JsonResponse({'status': 'success', 'message': '更新成功'})

    @login_auth(4)
    def delete(self, request):
        """
        删除
        """
        params = request.data
        uid = []
        info = params.get('info')
        if isinstance(info, dict):
            uid = info.get('uid')
            print(uid)
            UserInfo.objects.filter(uid=uid).delete()
        elif isinstance(info, list):
            for _ in info:
                uid.append(int(_.get('uid')))
            print(uid)
            UserInfo.objects.filter(uid__in=uid).delete()
        return JsonResponse('OK', safe=False)


class Test(GenericAPIView):
    """
    基础测试
    """

    @login_auth()
    def get(self, request):
        """
        查询
        """
        _data = []
        params = request.query_params.dict()
        _resp = TestInfo.objects.all().order_by('-uid')
        total = len(_resp)
        # 分页处理
        page = PageNumberPagination()
        page.page_size = params.get('page_size', '20')
        page_list = page.paginate_queryset(_resp, request, view=self)
        serializers = TestInfoSerializers(instance=page_list, many=True)
        for m in serializers.data:
            _data.append(dict(m))
        resp = JsonResponse({'total': total, 'data': _data})
        return resp

    @login_auth()
    def post(self, request):
        """
        新建
        """
        params = request.data
        if not params:
            return JsonResponse('没有数据', safe=False)
        # 初始化数据
        params['status'] = 'Pending'
        params['request_id'] = str(uuid.uuid4())
        serializer = TestInfoSerializers(data=params)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse({'status': 'success'})
        return JsonResponse({'status': 'fail', 'error': serializer.errors})

    @login_auth()
    def put(self, request):
        """
        更新
        """
        params = request.data
        if not params:
            return JsonResponse('没有数据', safe=False)
        uid = params.get('uid')
        if not uid:
            return JsonResponse('更新操作需指定UID', safe=False)
        resp_data = TestInfo.objects.filter(uid=uid).first()
        if not resp_data:
            fail_response['error'] = f'UID[{uid}]不存在!'
            return JsonResponse(fail_response)
        _data = TestInfoSerializers(instance=resp_data).data
        _data.update(params)
        serializers = TestInfoSerializers(instance=resp_data, data=_data)
        if serializers.is_valid():
            serializers.save()
            return JsonResponse(success_response)
        else:
            fail_response['message'] = serializers.errors
            return JsonResponse(fail_response)

    @login_auth()
    def delete(self, request):
        """
        删除
        """
        params = request.data
        uid = []
        info = params.get('info')
        if isinstance(info, dict):
            uid = info.get('uid')
            print(uid)
            TestInfo.objects.filter(uid=uid).delete()
        elif isinstance(info, list):
            for _ in info:
                uid.append(int(_.get('uid')))
            print(uid)
            TestInfo.objects.filter(uid__in=uid).delete()
        return JsonResponse('OK', safe=False)


class BaseCaseQueryData(GenericAPIView):
    """
    base页面过滤器
    """

    @login_auth()
    def get(self, request):
        """
        类型查询
        """
        _data = []
        params = request.query_params.dict()
        if params.get('filter'):
            if params.get('filter') == 'execute_user':
                info = UserInfo.objects.values_list('username', flat=True).distinct()
            else:
                info = TestInfo.objects.values_list(params.get('filter'), flat=True).distinct()
            if info:
                for _ in info:
                    _data.append({'text': _, 'value': _})
        return JsonResponse({'message': 'ok', 'data': _data})

    @login_auth()
    def post(self, request):
        """
        根据过滤条件获取数据
        """
        _data = []
        page_params = request.query_params.dict()
        params = request.data
        filter_fields = params.get('filter')
        if params.get('schedule_task_status'):
            test_info_obj = TestInfo.objects.filter(execute_time__isnull=False)
        else:
            test_info_obj = TestInfo.objects.filter(execute_time__isnull=True)
        query_info = None
        for filter in filter_fields:
            for key, value in filter.items():
                if not value:
                    continue
                if key == 'time':
                    query_info = test_info_obj.filter(
                        Q(create_time__gte=value.get('start_time')) & Q(modify_time__lte=value.get('end_time'))
                    ).order_by('-uid')
                elif key == 'execute_user':
                    if not query_info:
                        query_info = test_info_obj.filter(
                            execute_user__in=UserInfo.objects.filter(username__in=value)
                        ).order_by('-uid')
                    else:
                        query_info = test_info_obj.filter(execute_user__in=UserInfo.objects.filter(
                            username__in=value)
                        ).order_by('-uid')
                elif key == 'case_name':
                    if not query_info:
                        query_info = test_info_obj.filter(case_name__in=value).order_by('-uid')
                    else:
                        query_info = test_info_obj.filter(case_name__in=value).order_by('-uid')
                elif key == 'status':
                    if not query_info:
                        query_info = test_info_obj.filter(status__in=value).order_by('-uid')
                    else:
                        query_info = test_info_obj.filter(status__in=value).order_by('-uid')
                elif key == 'environment':
                    if not query_info:
                        query_info = test_info_obj.filter(environment__in=value).order_by('-uid')
                    else:
                        query_info = test_info_obj.filter(environment__in=value).order_by('-uid')

        if not query_info:
            query_info = test_info_obj.order_by('-uid')
        # 分页处理
        total = len(query_info)
        page = PageNumberPagination()
        page.page_size = page_params.get('page_size', '20')
        page_list = page.paginate_queryset(query_info, request, view=self)
        serializers = TestInfoSerializers(instance=page_list, many=True)
        for m in serializers.data:
            _data.append(dict(m))
        resp = JsonResponse({'total': total, 'data': _data})
        return resp


class UpdateBaseCase(GenericAPIView):
    """
    基础测试运行后数据更新API
    """

    def post(self, request):
        """
        更新数据API, 以 request_id 作为索引
        """
        request_data = request.data.dict()
        redis = fetch_redis()
        case_type = redis.hget(request_data.get('request_id'), 'case_type')
        if case_type == 'BaseSchedule':
            data = BaseScheduleInfo
        else:
            data = TestInfo
        _instance = data.objects.filter(request_id=request_data.get('request_id')).first()
        _data = TestInfoSerializers(instance=_instance).data
        if not _data:
            fail_response['message'] = 'TestInfo[request_id]不存在'
            return JsonResponse(fail_response)
        _data.update(request_data)
        _serializer = TestInfoSerializers(instance=_instance, data=_data)
        if _serializer.is_valid():
            _serializer.save()
            return JsonResponse(success_response)
        else:
            fail_response['message'] = _serializer.errors
            return JsonResponse(fail_response)


class BaseCaseExecute(GenericAPIView):
    """
    基础测试执行
    """
    @login_auth()
    def post(self, request):
        _data = request.data
        params = _data.get('data')
        if _data.get('method') == 'start':
            # 更新状态
            info= TestInfo.objects.filter(uid=params.get('uid')).first()
            if not info:
                fail_response['message'] = '未找到测试任务'
                return JsonResponse(fail_response)
            old_data = TestInfoSerializers(instance=info).data
            old_data['status'] = 'schedule'
            serializers = TestInfoSerializers(instance=info, data=old_data)
            if serializers.is_valid():
                serializers.save()
            if _data.get('schedule_task'):
                # 是定时任务就直接返回
                return JsonResponse(success_response)
            # 把测试数据写入缓存
            redis = fetch_redis()
            resp_data = dict(serializers.data)
            # 报警token 写入缓存
            police = ''
            if params.get('police'):
                _resp = Police.objects.filter(nickname=params.get('police')).first()
                police = _resp.token
            resp_data['police'] = police
            for key, value in resp_data.items():
                if not value:
                    continue
                redis.hset(info.request_id, key, value)

            # 把测试数据加入执行列表中
            send_task_data = {
                'case_file_name': info.case_name,
                'request_id': info.request_id,
            }
            base_run_test.delay(send_task_data)
            return JsonResponse(success_response)

        elif _data.get('method') == 'stop':
            # 1、获取测试进程pid
            uid = params.get('uid')
            test_info = TestInfo.objects.filter(uid=uid).first()
            if test_info.request_id:  # 为空时，说明还未调度就结束
                status, pid_list = self.get_pid(test_info.request_id)
                if not status:
                    return JsonResponse(pid_list)
                # 2、执行进程停止操作
                self.stop_process(pid_list)

            # 3、更新状态
            _data = TestInfoSerializers(instance=test_info).data
            _data['status'] = 'stop'
            _data['before_stop'] = test_info.status
            serializer = TestInfoSerializers(instance=test_info, data=_data)
            if serializer.is_valid():
                serializer.save()
            return JsonResponse(success_response)

        elif _data.get('method') == 'continue':
            # 继续执行
            uid = params.get('uid')
            test_info = TestInfo.objects.filter(uid=uid).first()
            if test_info.request_id:  # 为空时，说明还未调度就结束
                status, pid_list = self.get_pid(test_info.request_id)
                if not status:
                    return JsonResponse(pid_list)
                # 2、执行进程停止操作
                self.continue_process(pid_list)

            # 3、更新状态
            _data = TestInfoSerializers(instance=test_info).data
            _data['status'] = _data.get('before_stop')
            serializer = TestInfoSerializers(instance=test_info, data=_data)
            if serializer.is_valid():
                serializer.save()
            return JsonResponse(success_response)
        else:
            # 终止测试
            # 1、获取测试进程pid
            uid = params.get('uid')
            test_info = TestInfo.objects.filter(uid=uid).first()
            if test_info.request_id:  # 为空时，说明还未调度就结束
                status, pid_list = self.get_pid(test_info.request_id)
                if not status:
                    return JsonResponse(pid_list)
                # 2、执行进程停止操作
                self.delete_process(pid_list)

            # 3、更新状态
            _data = TestInfoSerializers(instance=test_info).data
            _data['status'] = 'kill'
            serializer = TestInfoSerializers(instance=test_info, data=_data)
            if serializer.is_valid():
                serializer.save()
            return JsonResponse(success_response)

    def stop_process(self, pid_list):
        for pid in pid_list:
            try:
                os.kill(int(pid), signal.SIGSTOP)
            except Exception as e:
                LOGGER.warning(e)
                raise e
        return True

    def continue_process(self, pid_list):
        for pid in pid_list:
            try:
                os.kill(int(pid), signal.SIGCONT)
            except Exception as e:
                LOGGER.warning(e)
                raise e
        return True

    def delete_process(self, pid_list):
        """
        测试终止
        """
        for pid in pid_list:
            try:
                # linux
                os.kill(int(pid), signal.SIGKILL)
                # windows
                # os.kill(int(pid), signal.SIGINT)
            except Exception as e:
                LOGGER.warning(e)
                raise e
        return True

    def get_pid(self, request_id):
        """ 获取进程pid """
        redis = fetch_redis()
        pid = redis.hget(constants.process_pid, request_id)
        if not pid:
            fail_response['message'] = f'request id[{request_id}] 没有相关进程'
            return False, fail_response
        return True, string_to_list(pid)


class BaseSchedule(GenericAPIView):
    """
    基础测试定时任务
    """

    @login_auth()
    def get(self, request):
        """ 查询 """
        _data = []
        params = request.query_params.dict()
        # 查询有效的定时任务
        _resp = BaseScheduleInfo.objects.filter(uniformity_status=0).order_by('-uid')
        total = len(_resp)
        # 分页处理
        page = PageNumberPagination()
        page.page_size = params.get('page_size', '20')
        page_list = page.paginate_queryset(_resp, request, view=self)
        serializers = BaseScheduleInfoSerializers(instance=page_list, many=True)
        for m in serializers.data:
            _data.append(dict(m))
        resp = JsonResponse({'total': total, 'data': _data})
        return resp

    @login_auth()
    def post(self, request):
        """ 新建 """
        params = request.data
        if not params:
            return JsonResponse('没有数据', safe=False)
        # 初始化数据
        params['status'] = 'Pending'
        params['request_id'] = str(uuid.uuid4())
        timeout = time.mktime(time.strptime(params.get('execute_time'), '%Y-%m-%d %X')) - time.time()
        if timeout < 2:
            return JsonResponse({'status': 'fail', 'error': '执行时间已过'})
        serializer = BaseScheduleInfoSerializers(data=params)
        if serializer.is_valid():
            serializer.save()

        # 加入延迟队列
        self.send(params['request_id'], int(timeout) * 1000)
        return JsonResponse({'status': 'success'})

    @login_auth()
    def put(self, request):
        """
        更新操作四种情况： 原调度时间 --> 1; 更新后调度时间 --> 2; 当前时间 --> 3
        第一种： 2 < 3  更新失败
        第二种： 1 == 2 > 3  直接更新
        第三种： 1 < 2 > 3  原测试任务标记为“丢弃”， 创建一条新的任务
        第四种： 1 > 2 > 3  原测试任务标记为“丢弃”， 创建一条新的任务
        """
        params = request.data
        if not params:
            return JsonResponse('没有数据', safe=False)
        uid = params.get('uid')
        if not uid:
            return JsonResponse('更新操作需指定UID', safe=False)
        resp_data = BaseScheduleInfo.objects.filter(uid=uid).first()
        if not resp_data:
            fail_response['error'] = f'UID[{uid}]不存在!'
            return JsonResponse(fail_response)
        _add_status = False
        now_time = datetime.datetime.now()
        _time = datetime.datetime.strftime(now_time, "%Y-%m-%d %H:%M:%S")
        old_time = datetime.datetime.strftime(resp_data.execute_time, "%Y-%m-%d %H:%M:%S")
        _data = BaseScheduleInfoSerializers(instance=resp_data).data
        if params.get('execute_time') < _time:
            fail_response['message'] = '更新失败, 测试已经开始'
            return JsonResponse(fail_response)

        if old_time == params.get('execute_time') and params.get('execute_time') > _time:
            _data.update(params)
        elif old_time > params.get('execute_time') and params.get('execute_time') > _time:
            _data['uniformity_status'] = 1
            _add_status = True
        elif old_time < params.get('execute_time') and params.get('execute_time') > _time:
            _data['uniformity_status'] = 1
            _add_status = True

        if _add_status:
            params['request_id'] = str(uuid.uuid4())
            serializer = BaseScheduleInfoSerializers(data=params)
            if serializer.is_valid():
                serializer.save()
            else:
                fail_response['message'] = serializer.errors
                return JsonResponse(fail_response)
            timeout = time.mktime(time.strptime(params.get('execute_time'), '%Y-%m-%d %X')) - time.time()
            self.send(params['request_id'], int(timeout) * 1000)

        serializers = BaseScheduleInfoSerializers(instance=resp_data, data=_data)
        if serializers.is_valid():
            serializers.save()
            return JsonResponse(success_response)
        else:
            fail_response['message'] = serializers.errors
            return JsonResponse(fail_response)

    @login_auth()
    def delete(self, request):
        """ 删除 """
        params = request.data
        uid = []
        info = params.get('info')
        if isinstance(info, dict):
            uid = info.get('uid')
            print(uid)
            BaseScheduleInfo.objects.filter(uid=uid).delete()
        elif isinstance(info, list):
            for _ in info:
                uid.append(int(_.get('uid')))
            print(uid)
            BaseScheduleInfo.objects.filter(uid__in=uid).delete()
        return JsonResponse('OK', safe=False)

    def send(self, request_id, timeout):
        # 加入延迟队列
        rabbit = RabbitConnection()
        channel = rabbit.get_connection()
        # 单位：毫秒
        send_task(channel,  request_id, timeout)
        # 关闭连接
        rabbit.connection.close()

class BaseScheduleQueryData(GenericAPIView):
    """
    base定时任务页面过滤器
    """

    def get(self, request):
        """
        类型查询
        """
        _data = []
        params = request.query_params.dict()
        if params.get('filter'):
            if params.get('filter') == 'execute_user':
                info = UserInfo.objects.values_list('username', flat=True).distinct()
            else:
                info = BaseScheduleInfo.objects.values_list(params.get('filter'), flat=True).distinct()
            if info:
                for _ in info:
                    _data.append({'text': _, 'value': _})
        return JsonResponse({'message': 'ok', 'data': _data})

    def post(self, request):
        """
        根据过滤条件获取数据
        """
        _data = []
        page_params = request.query_params.dict()
        params = request.data
        filter_fields = params.get('filter')
        if params.get('schedule_task_status'):
            test_info_obj = BaseScheduleInfo.objects.filter(execute_time__isnull=False)
        else:
            test_info_obj = BaseScheduleInfo.objects.filter(execute_time__isnull=True)
        query_info = None
        for filter in filter_fields:
            for key, value in filter.items():
                if not value:
                    continue
                if key == 'time':
                    query_info = test_info_obj.filter(
                        Q(create_time__gte=value.get('start_time')) & Q(modify_time__lte=value.get('end_time'))
                    ).order_by('-uid')
                elif key == 'execute_user':
                    if not query_info:
                        query_info = test_info_obj.filter(
                            execute_user__in=UserInfo.objects.filter(username__in=value)
                        ).order_by('-uid')
                    else:
                        query_info = test_info_obj.filter(execute_user__in=UserInfo.objects.filter(
                            username__in=value)
                        ).order_by('-uid')
                elif key == 'case_name':
                    if not query_info:
                        query_info = test_info_obj.filter(case_name__in=value).order_by('-uid')
                    else:
                        query_info = test_info_obj.filter(case_name__in=value).order_by('-uid')
                elif key == 'status':
                    if not query_info:
                        query_info = test_info_obj.filter(status__in=value).order_by('-uid')
                    else:
                        query_info = test_info_obj.filter(status__in=value).order_by('-uid')
                elif key == 'environment':
                    if not query_info:
                        query_info = test_info_obj.filter(environment__in=value).order_by('-uid')
                    else:
                        query_info = test_info_obj.filter(environment__in=value).order_by('-uid')

        if not query_info:
            query_info = test_info_obj.order_by('-uid')
        # 分页处理
        total = len(query_info)
        page = PageNumberPagination()
        page.page_size = page_params.get('page_size', '20')
        page_list = page.paginate_queryset(query_info, request, view=self)
        serializers = BaseScheduleInfoSerializers(instance=page_list, many=True)
        for m in serializers.data:
            _data.append(dict(m))
        resp = JsonResponse({'total': total, 'data': _data})
        return resp


class BaseScheduleExecute(GenericAPIView):
    """
    基础测试执行
    """
    def post(self, request):
        """
        执行定时任务
        """
        params = request.data
        redis = fetch_redis()
        # 获取uniformity_status 为0的
        task_data = BaseScheduleInfo.objects.filter(request_id=params.get('request_id'))\
            .filter(uniformity_status=0).first()
        if not task_data:
            success_response['message'] = '当前时间没有可执行任务'
            return JsonResponse(success_response)

        _data = BaseScheduleInfoSerializers(instance=task_data).data
        _data['status'] = 'schedule'
        serializers = BaseScheduleInfoSerializers(instance=task_data, data=_data)
        if serializers.is_valid():
            serializers.save()
        serializer_data = dict(serializers.data)
        if serializer_data.get('police'):
            # 报警token 写入缓存
            _resp = Police.objects.filter(nickname=_data.get('police')).first()
            serializer_data['police'] = _resp.token
        # 与基础测试区分
        serializer_data['case_type'] = 'BaseSchedule'
        for key, value in serializer_data.items():
            if not value:
                continue
            redis.hset(_data.get('request_id'), key, value)

        # 把测试数据加入执行列表中
        send_task_data = {
            'case_file_name': _data.get('case_name'),
            'request_id': _data.get('request_id'),
        }
        base_run_test.delay(send_task_data)
        return JsonResponse(success_response)


class TestCaseManage(GenericAPIView):
    """
    测试用例管理
    """

    @login_auth()
    def get(self, request):
        """
        获取所有用例
        """
        _data = []
        params = request.query_params.dict()
        _resp = CaseManage.objects.all().order_by('-uid')
        # 分页处理
        total = len(_resp)
        page = PageNumberPagination()
        page.page_size = params.get('page_size', '20')
        page_list = page.paginate_queryset(_resp, request, view=self)
        serializers = CaseManageSerializers(instance=page_list, many=True)
        for m in serializers.data:
            _data.append(dict(m))
        resp = JsonResponse({'code': 200, 'total': total, 'data': _data})
        return resp

    @login_auth(4)
    def post(self, request):
        """
        新增
        """
        params = request.data
        if not params:
            return JsonResponse('没有数据', safe=False)
        serializer = CaseManageSerializers(data=params)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(success_response)
        fail_response['error'] = serializer.errors
        return JsonResponse(fail_response)

    @login_auth(4)
    def put(self, request):
        data = request.data
        uid = data.get('uid')
        if not uid:
            fail_response['error'] = '请指定的测试用例不存在'
            return JsonResponse(fail_response)
        info = CaseManage.objects.filter(uid=uid).first()
        if not info:
            fail_response['error'] = f'UID[{uid}]不存在!'
            return JsonResponse(fail_response)
        serializer = CaseManageSerializers(instance=info, data=data)
        if serializer.is_valid():
            serializer.save()
        else:
            fail_response['error'] = serializer.errors
            return JsonResponse(fail_response)
        return JsonResponse(success_response)

    @login_auth(4)
    def delete(self, request):
        """
        删除
        """
        params = request.data
        uid = []
        info = params.get('info')
        if isinstance(info, dict):
            uid = info.get('uid')
            print(uid)
            CaseManage.objects.filter(uid=uid).delete()
        elif isinstance(info, list):
            for _ in info:
                uid.append(int(_.get('uid')))
            print(uid)
            CaseManage.objects.filter(uid__in=uid).delete()
        return JsonResponse('OK', safe=False)


class TestVersionManage(GenericAPIView):
    """
    基础测试
    """

    @login_auth()
    def get(self, request):
        """
        查询
        """
        _data = []
        params = request.query_params.dict()
        time_filter = params.get('time_filter')
        if time_filter:
            time_filter = eval(time_filter)
            _resp = VersionManage.objects.filter(
                Q(create_time__gte=time_filter.get('start_time')) & Q(modify_time__lte=time_filter.get('end_time'))
            ).order_by('-uid')
        else:
            _resp = VersionManage.objects.all().order_by('-uid')
        total = len(_resp)
        # 分页处理
        page = PageNumberPagination()
        page.page_size = params.get('page_size', '20')
        page_list = page.paginate_queryset(_resp, request, view=self)
        serializers = VersionManageSerializers(instance=page_list, many=True)
        for m in serializers.data:
            _data.append(dict(m))
        resp = JsonResponse({'total': total, 'data': _data})
        return resp

    @login_auth(4)
    def post(self, request):
        """
        新建
        """
        params = request.data
        if not params:
            return JsonResponse('没有数据', safe=False)
        serializer = VersionManageSerializers(data=params)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(success_response)
        if 'already exists' in serializer.errors.get('version_name', '').__str__():
            fail_response['error'] = '版本号已存在'
        else:
            fail_response['error'] = serializer.errors
        return JsonResponse(fail_response)

    @login_auth(4)
    def delete(self, request):
        """
        删除
        """
        params = request.data
        uid = []
        info = params.get('info')
        if isinstance(info, dict):
            uid = info.get('uid')
            VersionManage.objects.filter(uid=uid).delete()
        elif isinstance(info, list):
            for _ in info:
                uid.append(int(_.get('uid')))
            VersionManage.objects.filter(uid__in=uid).delete()
        return JsonResponse('OK', safe=False)


class TestPackManage(GenericAPIView):
    """
    版本安装包管理
    """

    @login_auth()
    def get(self, request):
        """
        查询
        """
        _data = []
        params = request.query_params.dict()
        _resp = PackMange.objects.all().order_by('-uid')
        total = len(_resp)
        # 分页处理
        page = PageNumberPagination()
        page.page_size = params.get('page_size', '20')
        page_list = page.paginate_queryset(_resp, request, view=self)
        serializers = PackMangeSerializers(instance=page_list, many=True)
        for m in serializers.data:
            _data.append(dict(m))
        resp = JsonResponse({'total': total, 'data': _data})
        return resp

    @login_auth(4)
    def post(self, request):
        """
        新建
        """
        params = request.data
        if not params:
            return JsonResponse('没有数据', safe=False)
        for i in params.get('package_list'):
            i['version'] = params.get('version')
            serializer = PackMangeSerializers(data=i)
            if serializer.is_valid():
                serializer.save()
            else:
                break
        else:
            return JsonResponse(success_response)
        fail_response['error'] = serializer.errors
        return JsonResponse(fail_response)


class RegressionTest(GenericAPIView):
    """
    回归测试
    """

    @login_auth()
    def get(self, request):
        """
        获取所有用例
        """
        _data = []
        _resp = []
        params = request.query_params.dict()
        if not params.get('method'):
            _resp = Regression.objects.all().order_by('-uid')
        else:
            # 条件查询
            if params.get('method') == 'get_version':
                _resp = VersionManage.objects.values('version_name').distinct()
                serializers = VersionManageSerializers(instance=_resp, many=True)
            elif params.get('method') == 'get_case_name':
                _resp = CaseManage.objects.values('case_name').distinct()
                serializers = CaseManageSerializers(instance=_resp, many=True)
            elif params.get('method') == 'uid':
                _resp = Regression.objects.filter(uid=params.get('uid')).first()
                serializers = RegressionSerializers(instance=_resp)
                success_response['data'] = serializers.data
                return JsonResponse(success_response)
            elif params.get('method') == 'regression':
                _resp = RegressionTestInfo.objects.filter(regression=params.get('regression'))
                serializers = RegressionTestInfoSerializers(instance=_resp, many=True)
                success_response['data'] = serializers.data
                return JsonResponse(success_response)
            for m in serializers.data:
                _data.append(dict(m))
            success_response['data'] = _data
            return JsonResponse(success_response)
        # 分页处理
        total = len(_resp)
        page = PageNumberPagination()
        page.page_size = params.get('page_size', '20')
        page_list = page.paginate_queryset(_resp, request, view=self)
        serializers = RegressionSerializers(instance=page_list, many=True)
        for m in serializers.data:
            _data.append(dict(m))
        success_response['data'] = _data
        success_response['total'] = total
        return JsonResponse(success_response)

    @login_auth()
    def post(self, request):
        """
        新增
        """
        params = request.data
        if not params:
            return JsonResponse('没有数据', safe=False)
        # 获取测试 case 列表
        case_name_list = params.get('case_name_list').split(',')
        # 插入数据到regression
        params['status'] = 'Pending'
        params['case_number'] = len(case_name_list)
        serializer = RegressionSerializers(data=params)
        if serializer.is_valid():
            serializer.save()
            regression = dict(serializer.data)
        else:
            fail_response['error'] = serializer.errors
            return JsonResponse(fail_response)
        # 生成相应的case数据
        case_name_list = params.get('case_name_list').split(',')
        test_info = {
            'status': 'Pending',
            'execute_user': regression.get('execute_user'),
            'regression': regression.get('uid'),
        }
        for case_name in case_name_list:
            test_info['case_name'] = case_name
            _serializer = RegressionTestInfoSerializers(data=test_info)
            if _serializer.is_valid():
                _serializer.save()
            else:
                fail_response['error'] = serializer.errors
                return JsonResponse(fail_response)
        return JsonResponse(success_response)

    @login_auth()
    def put(self, request):
        """
        修改
        """
        data = request.data
        uid = data.get('uid')
        case_name_list = data.get('case_name_list').split(',')
        data['case_number'] = len(case_name_list)
        if not uid:
            fail_response['error'] = '请指定的测试用例UID'
            return JsonResponse(fail_response)
        info = Regression.objects.filter(uid=uid).first()
        if not info:
            fail_response['error'] = f'UID[{uid}]不存在!'
            return JsonResponse(fail_response)
        # 更新Regression
        serializer = RegressionSerializers(instance=info, data=data)
        if serializer.is_valid():
            serializer.save()
            regression = dict(serializer.data)
        else:
            fail_response['error'] = serializer.errors
            return JsonResponse(fail_response)
        # 更新RegressionTestInfo
        test_info = {
            'status': 'Pending',
            'execute_user': regression.get('execute_user'),
            'regression': regression.get('uid'),
        }
        case_list = data.get('case_name_list').split(',')
        query_data_list = RegressionTestInfo.objects.filter(regression=uid)
        query_case_name_list = []
        # 删减
        for query_data in query_data_list:
            if query_data.case_name not in case_list:
                query_data.delete()
            query_case_name_list.append(query_data.case_name)
        # 添加
        for case_name in case_list:
            if case_name not in query_case_name_list:
                test_info['case_name'] = case_name
                _serializer = RegressionTestInfoSerializers(data=test_info)
                if _serializer.is_valid():
                    _serializer.save()
        return JsonResponse(success_response)

    @login_auth()
    def delete(self, request):
        """
        删除
        """
        params = request.data
        uid = []
        info = params.get('info')
        if isinstance(info, dict):
            uid = info.get('uid')
            print(uid)
            Regression.objects.filter(uid=uid).delete()
        elif isinstance(info, list):
            for _ in info:
                uid.append(int(_.get('uid')))
            print(uid)
            Regression.objects.filter(uid__in=uid).delete()
        return JsonResponse('OK', safe=False)


class RegressionExecute(GenericAPIView):
    """
    回归测试执行调度
    """

    def post(self, request):
        """
        启动、停止/继续、 结束
        """
        params = request.data
        data = params.get('data')
        if not params:
            return JsonResponse('没有数据', safe=False)
        if params.get('method') == 'start':
            # 启动测试
            resp_info = self.start(data)
            return JsonResponse(resp_info)
        elif params.get('method') == 'stop':
            stop_data = {'status': 'stop'}
            # 1、获取测试进程pid
            regression_uid = data.get('uid')
            regression_test_info = RegressionTestInfo.objects.filter(regression=regression_uid)
            regression_test_info = list(regression_test_info)
            for instance in regression_test_info:
                if instance.request_id:  # 为空时，说明还未调度就结束
                    status, pid_list = self.get_pid(instance.request_id)
                    if not status:
                        continue
                    status, info = self.stop_process(pid_list)
                    if not status:
                        LOGGER.error(f'回归测试UID: [{data.get("uid")}], 执行stop失败, error: {info}')
                    else:
                        # 更新 RegressionTestInfo 状态
                        stop_data['before_stop'] = instance.status
                        update_data = RegressionTestInfoSerializers(instance=instance).data
                        update_data.update(stop_data)
                        test_info_serializer = RegressionTestInfoSerializers(
                            instance=instance, data=update_data
                        )
                        if test_info_serializer.is_valid():
                            test_info_serializer.save()

            # 更新 Regression 状态
            regression = Regression.objects.filter(uid=data.get('uid')).first()
            stop_data['before_stop'] = regression.status
            update_data = RegressionSerializers(instance=regression).data
            update_data.update(stop_data)
            regression_serializers = RegressionSerializers(instance=regression, data=update_data)
            if regression_serializers.is_valid():
                regression_serializers.save()
            return JsonResponse(success_response)
        elif params.get('method') == 'continue':
            # 继续执行
            # 1、获取测试进程pid
            regression_uid = data.get('uid')
            regression_test_info = RegressionTestInfo.objects.filter(regression=regression_uid)
            regression_test_info = list(regression_test_info)
            for instance in regression_test_info:
                if instance.request_id:  # 为空时，说明还未调度就结束
                    status, pid_list = self.get_pid(instance.request_id)
                    if not status:
                        continue
                    status, info = self.continue_process(pid_list)
                    if not status:
                        LOGGER.error(f'回归测试UID: [{data.get("uid")}], 执行continue失败, error: {info}')
                    else:
                        # 更新 RegressionTestInfo
                        continue_data = {'status': instance.before_stop}
                        update_data = RegressionTestInfoSerializers(instance=instance).data
                        update_data.update(continue_data)
                        test_info_serializer = RegressionTestInfoSerializers(
                            instance=instance, data=update_data
                        )
                        if test_info_serializer.is_valid():
                            test_info_serializer.save()
            # 更新 Regression 的status
            regression = Regression.objects.filter(uid=data.get('uid')).first()
            continue_data = {'status': regression.before_stop}
            update_data = RegressionSerializers(instance=regression).data
            update_data.update(continue_data)
            regression_serializers = RegressionSerializers(instance=regression, data=update_data)
            if regression_serializers.is_valid():
                regression_serializers.save()
            return JsonResponse(success_response)
        else:
            # 终止测试
            _data = {'status': 'kill'}  # 更新状态
            regression_uid = data.get('uid')
            regression_test_info = RegressionTestInfo.objects.filter(regression=regression_uid)
            regression_test_info = list(regression_test_info)
            for instance in regression_test_info:
                if instance.request_id:  # 为空时，说明还未调度就结束
                    status, pid_list = self.get_pid(instance.request_id)
                    if not status:
                        continue
                    status, info = self.delete_process(pid_list)
                    if not status:
                        LOGGER.error(f'回归测试UID: [{data.get("uid")}], 执行stop失败, error: {info}')
                    else:
                        # 更新
                        update_data = RegressionTestInfoSerializers(instance=instance).data
                        update_data.update(_data)
                        test_info_serializer = RegressionTestInfoSerializers(instance=instance, data=update_data)
                        if test_info_serializer.is_valid():
                            test_info_serializer.save()
            # 更新
            regression = Regression.objects.filter(uid=data.get('uid')).first()
            update_data = RegressionSerializers(instance=regression).data
            update_data.update(_data)
            regression_serializers = RegressionSerializers(instance=regression, data=update_data)
            if regression_serializers.is_valid():
                regression_serializers.save()
            return JsonResponse(success_response)

    def stop_process(self, pid_list):
        """ 停止 """
        for pid in pid_list:
            try:
                os.kill(int(pid), signal.SIGSTOP)
            except Exception as e:
                LOGGER.warning(e)
                return False, e
        return True, ''

    def continue_process(self, pid_list):
        """ 继续 """
        for pid in pid_list:
            try:
                os.kill(int(pid), signal.SIGCONT)
            except Exception as e:
                LOGGER.warning(e)
                return False, e
        return True, ''

    def delete_process(self, pid_list):
        """ 终止 """
        for pid in pid_list:
            try:
                # linux
                # os.kill(int(pid), signal.SIGKILL)
                # windows
                os.kill(int(pid), signal.SIGINT)
            except Exception as e:
                LOGGER.warning(e)
                return False, e
        return True, ''

    def get_pid(self, request_id):
        """ 获取进程pid """
        redis = fetch_redis()
        pid = redis.hget(constants.process_pid, request_id)
        if not pid:
            fail_response['message'] = f'request id[{request_id}] 没有相关进程'
            return False, fail_response
        return True, string_to_list(pid)

    def start(self, task_info):
        # 执行测试、 更新状态
        instance = Regression.objects.filter(uid=task_info.get('uid')).first()
        regression_data = RegressionSerializers(instance=instance).data
        regression_data.update({'status': 'running'})
        serializer = RegressionSerializers(instance=instance, data=regression_data)
        if serializer.is_valid():
            serializer.save()

        redis = fetch_redis()
        # 设置测试用例数, 最后一个用例执行完回调
        case_number_query_key = str(uuid.uuid4())
        redis.set(case_number_query_key, regression_data.get('case_number'))

        # 更新 RegressionTestInfo 表状态为schedule
        execute_data_list = []
        test_info_list = RegressionTestInfo.objects.filter(regression=task_info.get('uid'))
        for test_info in test_info_list:
            test_info_serializer = RegressionTestInfoSerializers(instance=test_info)
            info = test_info_serializer.data
            info['status'] = 'schedule'
            info['request_id'] = str(uuid.uuid4())
            info_serializer = RegressionTestInfoSerializers(instance=test_info, data=info)
            if info_serializer.is_valid():
                info_serializer.save()
            else:
                fail_response['message'] = serializer.errors
                return fail_response
            # 获取用例文件
            case_manage = CaseManage.objects.filter(case_name=info.get('case_name')).first()
            if not case_manage:
                fail_response['message'] = f'测试用例 [{info.get("case_name")}] 不存在, 请管理员'
                return fail_response
            case_file_name = case_manage.case_file_name
            if not case_file_name:
                fail_response['message'] = f'测试用例 [{info.get("case_name")}] 没有添加执行命令, 请管理员'
                return fail_response
            # 把测试数据加入执行列表中
            execute_data_list.append({
                'case_file_name': case_file_name,
                'request_id': info['request_id'],
            })
            # 把测试数据写入缓存
            params = dict(info_serializer.data)
            params['case_number_query_key'] = case_number_query_key
            # 报警token 写入缓存
            police = ''
            if regression_data.get('police'):
                _resp = Police.objects.filter(nickname=regression_data.get('police')).first()
                police = _resp.token
            params['police'] = police
            for key, value in params.items():
                if not value:
                    continue
                redis.hset(info['request_id'], key, value)
        # 执行测试
        if not execute_data_list:
            return success_response
        for test_params in execute_data_list:
            regression_run_test.delay(test_params)
        return success_response


class UpdateRegression(GenericAPIView):
    """
    回归测试运行后数据更新API
    """

    def post(self, request):
        """
        更新数据API, 以uid 作为索引
        """
        data = request.data.dict()
        # 更新主任务表
        regression = Regression.objects.filter(uid=data.get('regression')).first()
        _data = RegressionSerializers(instance=regression).data
        if not _data:
            fail_response['message'] = 'regression[uid]不存在'
            return JsonResponse(fail_response)
        _data.update(data)
        _serializer = RegressionSerializers(instance=regression, data=_data)
        if _serializer.is_valid():
            _serializer.save()
            return JsonResponse(success_response)
        else:
            fail_response['message'] = _serializer.errors
            return JsonResponse(fail_response)


class UpdateRegressionInfo(GenericAPIView):
    """
    回归测试运行后数据更新API
    """

    def post(self, request):
        """
        更新数据API, 以uid and request_id 作为索引
        """
        query_set = None
        data = request.data.dict()
        if data.get('uid'):
            query_set = RegressionTestInfo.objects.filter(uid=data.get('uid')).first()
        elif data.get('request_id'):
            query_set = RegressionTestInfo.objects.filter(request_id=data.get('request_id')).first()
        if not query_set:
            fail_response['message'] = '测试[uid] 不存在'
            return JsonResponse(fail_response)
        # 获取数据
        new_data = RegressionTestInfoSerializers(instance=query_set).data
        # 更新数据
        new_data.update(data)
        # 插入数据库
        serializer = RegressionTestInfoSerializers(instance=query_set, data=new_data)
        if serializer.is_valid():
            serializer.save()
        else:
            fail_response['message'] = serializer.errors
            return JsonResponse(fail_response)
        return JsonResponse(success_response)


class ReadLog(GenericAPIView):
    """
    读取日志文件
    """
    @login_auth()
    def post(self, request):
        params = request.data
        file_path = params.get('file_path')
        if not file_path:
            fail_response['message'] = '不存在'
            return JsonResponse(fail_response)
        if 'html' in file_path:
            with open(file_path, 'rb') as f:
                content = f.read()
        else:
            with open(file_path, 'r') as f:
                content = f.read()
        return HttpResponse(content)


class PoliceManage(GenericAPIView):
    """
    报警机器人管理
    """

    @login_auth()
    def get(self, request):
        """ 获取报警机器人列表 """
        _data = []
        params = request.query_params.dict()
        if params.get('filter') == 'nickname':
            _resp = Police.objects.values_list('nickname').distinct()
            if _resp:
                for _ in _resp:
                    _data.append({'nickname': _})
            success_response['data'] = _data
            return JsonResponse(success_response)

        _resp = Police.objects.all().order_by('-uid')
        # 分页处理
        total = len(_resp)
        page = PageNumberPagination()
        page.page_size = params.get('page_size', '20')
        page_list = page.paginate_queryset(_resp, request, view=self)
        serializers = PoliceSerializers(instance=page_list, many=True)
        for m in serializers.data:
            _data.append(dict(m))
        success_response.update({'total': total, 'data': _data})
        return JsonResponse(success_response)

    @login_auth(4)
    def post(self, request):
        """
        新增
        """
        data = request.data
        police = Police.objects.filter(token=data.get('token')).first()
        if police:
            fail_response['message'] = f'机器人已存在, nickname: [{police.nickname}]'
            return JsonResponse(fail_response)
        serializer = PoliceSerializers(data=data)
        if serializer.is_valid():
            serializer.save()
        else:
            fail_response['message'] = serializer.errors
            return JsonResponse(fail_response)
        return JsonResponse(success_response)

    @login_auth(4)
    def put(self, request):
        """ 修改 """
        data = request.data
        uid = data.get('uid')
        info = Police.objects.filter(uid=uid).first()
        if not info:
            fail_response['error'] = f'UID[{uid}]机器人不存在!'
            return JsonResponse(fail_response)
        serializer = PoliceSerializers(instance=info, data=data)
        if serializer.is_valid():
            serializer.save()
        else:
            fail_response['message'] = serializer.errors
            return JsonResponse(fail_response)
        return JsonResponse(success_response)

    @login_auth(4)
    def delete(self, request):
        """
        删除
        """
        params = request.data
        uid = []
        info = params.get('info')
        if isinstance(info, dict):
            uid = info.get('uid')
            Police.objects.filter(uid=uid).delete()
        elif isinstance(info, list):
            for _ in info:
                uid.append(int(_.get('uid')))
            Police.objects.filter(uid__in=uid).delete()
        return JsonResponse(success_response)


class LogManage(GenericAPIView):
    """
    获取目录下文件
    """

    @login_auth()
    def get(self, request):
        """
        查看
        :param request:
        :return:
        """
        params = request.query_params
        if not params.get('path'):
            fail_response['message'] = '请指定path'
            return JsonResponse(fail_response)
        msg = self.check_dir(params.get('path'))
        if not msg:
            success_response['message'] = '文件夹为空'
            success_response['data'] = []
            return JsonResponse(success_response)
        resp_list = msg.split('\n')
        log_path_list = []
        for i in resp_list:
            i = i.split(' ')
            _data = {
                'type': '文件夹' if 'd' in i[0] else '文本文档',
                'size': i[1],
                'time': i[2:5],
                'name': i[5],
            }
            log_path_list.append(_data)
        success_response['data'] = log_path_list
        return JsonResponse(success_response)

    @login_auth()
    def post(self, request):
        """
        打开文件
        :param request:
        :return:
        """
        params = request.data
        if not params.get('path'):
            fail_response['message'] = '请指定path'
            return JsonResponse(fail_response)
        if not os.path.isfile(params.get('path')):
            fail_response['message'] = '文件打开失败'
            return JsonResponse(fail_response)
        with open(f'{params.get("path")}', 'r') as f:
            text = f.read()
        success_response['data'] = text
        return JsonResponse(success_response)

    @login_auth(5)
    def delete(self, request):
        """
        删除
        :param request:
        :return:
        """
        params = request.data
        if not params.get('path'):
            fail_response['message'] = '请指定path'
            return JsonResponse(fail_response)
        cmd = f'rm -rf {params.get("path")}'
        self.execute(cmd)
        return JsonResponse(success_response)

    def execute(self, cmd):
        res = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, encoding='utf-8')
        outs, errs = res.communicate()
        try:
            msg = outs.strip('\n')
        except Exception:
            msg = None
        return msg

    def check_dir(self, path):
        """
        执行
        """
        cmd = "ls -l %s|tail -n +2|awk '{print $1,$5,$6,$7,$8,$9}'" % path
        return self.execute(cmd)

